#include "dev_eeprom.h"

#define EEPROM_I2C_BUS_NAME    "i2c2"          /* 传感器连接的I2C总线设备名称 */
#define EEPROM_I2C_FIX_ADDR    (0xA0 >> 1)     /* 固定从机地址 (在rt-thread驱动中, 会自动 << 1) */

/* eeprom 设备管理结构体 */
static dev_rtdrv_eeprom_t s_dev_rtdrv_eeprom;

/* 找到i2c控制总线 */
static RET_STATUS s_dev_eeprom_find_i2c_bus(dev_rtdrv_eeprom_t rtdrv_eeprom)
{
    const char *i2c_bus_name = EEPROM_I2C_BUS_NAME;

    rtdrv_eeprom->i2c_bus = (struct rt_i2c_bus_device *) rt_device_find(EEPROM_I2C_BUS_NAME);

    if (rtdrv_eeprom->i2c_bus == RT_NULL)
    {
        rt_kprintf("can't find %s device!\n", i2c_bus_name);

        return RET_ERROR;
    }

    /* 设置从机地址 */
    rtdrv_eeprom->fix_addr = EEPROM_I2C_FIX_ADDR;

    return RET_OK;
}

/* EEPROM 初始化 */
RET_STATUS dev_eeprom_init(void)
{
    /* 分配设备管理结构体 */
    s_dev_rtdrv_eeprom = (dev_rtdrv_eeprom_t) rt_calloc(1, sizeof(dev_rtdrv_eeprom));

    if (s_dev_rtdrv_eeprom == NULL)
    {
        return RET_ERROR;
    }

    /* 找到i2c控制总线 */
    if (s_dev_eeprom_find_i2c_bus(s_dev_rtdrv_eeprom) != RET_OK)
    {
        goto fail_handler;
    }

    /* 确定设备的每页字节数和最大地址值 */
#if (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_01)
    s_dev_rtdrv_eeprom->capacity_kbit = 1;
    s_dev_rtdrv_eeprom->max_addr      = 128 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 8;
    s_dev_rtdrv_eeprom->page_nbr      = 16;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_02)
    s_dev_rtdrv_eeprom->capacity_kbit = 2;
    s_dev_rtdrv_eeprom->max_addr      = 256 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 8;
    s_dev_rtdrv_eeprom->page_nbr      = 32;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_04)
    s_dev_rtdrv_eeprom->capacity_kbit = 4;
    s_dev_rtdrv_eeprom->max_addr      = 512 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 16;
    s_dev_rtdrv_eeprom->page_nbr      = 32;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_08)
    s_dev_rtdrv_eeprom->capacity_kbit = 8;
    s_dev_rtdrv_eeprom->max_addr      = 1024 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 16;
    s_dev_rtdrv_eeprom->page_nbr      = 64;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_16)
    s_dev_rtdrv_eeprom->capacity_kbit = 16;
    s_dev_rtdrv_eeprom->max_addr      = 2048 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 16;
    s_dev_rtdrv_eeprom->page_nbr      = 128;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_32)
    s_dev_rtdrv_eeprom->capacity_kbit = 32;
    s_dev_rtdrv_eeprom->max_addr      = 4096 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 32;
    s_dev_rtdrv_eeprom->page_nbr      = 128;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_64)
    s_dev_rtdrv_eeprom->capacity_kbit = 64;
    s_dev_rtdrv_eeprom->max_addr      = 8192 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 32;
    s_dev_rtdrv_eeprom->page_nbr      = 256;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_128)
    s_dev_rtdrv_eeprom->capacity_kbit = 128;
    s_dev_rtdrv_eeprom->max_addr      = 16384 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 64;
    s_dev_rtdrv_eeprom->page_nbr      = 256;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_256)
    s_dev_rtdrv_eeprom->capacity_kbit = 256;
    s_dev_rtdrv_eeprom->max_addr      = 32768 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 64;
    s_dev_rtdrv_eeprom->page_nbr      = 512;
#elif (DEV_EEPROM_TYPE == DEV_EEPROM_TYPE_512)
    s_dev_rtdrv_eeprom->capacity_kbit = 512;
    s_dev_rtdrv_eeprom->max_addr      = 65536 - 1;
    s_dev_rtdrv_eeprom->page_byte     = 128;
    s_dev_rtdrv_eeprom->page_nbr      = 512;
#endif

    /* 
     * 初始化增补地址为0, 增补地址说明 (对于 <= 16 的存储器 有效):
     * 1) 对于 > 16Kbit 的型号, 目标存储地址是2字节表示, 即发送从机地址(0xA0), 再发送目标存储地址.
     *    (Ax 和 管脚电平有关)
     *    > 16K: 1 0 1 0 A2 A1 A0 R/W
     * 2) 对于 <= 16Kbit 的型号, 从机地址是0xA0合并目标存储地址所在的页序号, 再发送目标存储地址在该页内偏移地址.
     *    (Ax 和 管脚电平有关; P0即 目标存储地址所在的地址页序号(每页256Byte, 区别于实际页的情况))
     *    1K/2K: 1 0 1 0 A2 A1 A0 R/W
     *    4K   : 1 0 1 0 A2 A1 P0 R/W
     *    8K   : 1 0 1 0 A2 P1 P0 R/W
     *    16K  : 1 0 1 0 P2 P1 P0 R/W
     *
     *    示例: 8K设备存储地址范围 0-1023, 如果目标存储地址为 999, 则从机地址和增补地址分别为:
     *          999 / 256 = 3.90234375, 即目标存储地址在 第3页
     *          fix_addr = (0x50 << 1) | (3 << 1) = 0xA6 | R/W   (1 0 1 0 0 1 1 R/W)
     *          sub_addr   = 999 & 0xFF = 0xE7
     */
    s_dev_rtdrv_eeprom->sub_addr = 0x00;

    /* 初始化成功 */
    s_dev_rtdrv_eeprom->init_status = INIT_SUCC;
    return RET_OK;

    /* 初始化失败 */
fail_handler:

    rt_free(s_dev_rtdrv_eeprom); /* 释放分配到的内存 */
    s_dev_rtdrv_eeprom->init_status = INIT_FAIL;
    return RET_ERROR;
}

/* 显示EEPROM设备信息 */
void dev_eeprom_show_info(void)
{
    /* 判断设备管理指针有效性 */
    if (s_dev_rtdrv_eeprom == NULL)
    {
        rt_kprintf("s_dev_rtdrv_eeprom is NULL\r\n");
        return;
    }

    /* 判断设备是否完成初始化 */
    if (s_dev_rtdrv_eeprom->init_status != INIT_SUCC)
    {
        rt_kprintf("eeprom is uninitialized\r\n");
        return;
    }

    /* 输出设备信息 */
    rt_kprintf("EEPROM INFO: \r\n");
    rt_kprintf("capacity(kbit) = %d\r\n", s_dev_rtdrv_eeprom->capacity_kbit);
    rt_kprintf("max addr = %d\r\n", s_dev_rtdrv_eeprom->max_addr);
    rt_kprintf("page byte = %d\r\n", s_dev_rtdrv_eeprom->page_byte);
    rt_kprintf("page number = %d\r\n", s_dev_rtdrv_eeprom->page_nbr);

    return;
}

/* EEPROM 写一个字节(精度: ) */
RET_STATUS dev_eeprom_write_one_byte(uint16_t wr_addr, uint8_t data)
{
    uint8_t   buf[3];

    /* 检验目标存储地址范围有效性 */
    if (wr_addr > s_dev_rtdrv_eeprom->max_addr)
    {
        return RET_ERROR;   /* 地址超出范围 */
    }
    
    /* 判断设备初始化状态 */
    if (s_dev_rtdrv_eeprom->init_status != INIT_SUCC)
    {
        return RET_ERROR; /* 初始化失败, 禁止使用设备 */
    }

    /* 判断EEPROM设备类型, 发送要读的目标存储地址和要写入的数据 */
#if (DEV_EEPROM_TYPE > DEV_EEPROM_TYPE_16)
    {
        /* 构建数据并发送 */
        buf[0] = (wr_addr & 0xFF00) >> 8;   /* 目标存储地址 高地址 */
        buf[1] = (wr_addr & 0xFF);          /* 目标存储地址 低地址 */
        buf[2] = data;                      /* 要写的数据 */
        
        if (rt_i2c_master_send(s_dev_rtdrv_eeprom->i2c_bus,
                               s_dev_rtdrv_eeprom->fix_addr,
                               RT_I2C_WR,
                               buf, 3) != 3)
        {
            return RET_ERROR;
        }
    }
#else
    {
        /* 构建数据并发送 */
        s_dev_rtdrv_eeprom->sub_addr = (wr_addr / 256) ;  /* 计算增补地址 (在rt-thread驱动中, 会自动 << 1) */
        buf[0] = (wr_addr % 256);           /* 计算地址在所在页的偏移地址 */
        buf[1] = data;                      /* 要写的数据 */

        if (rt_i2c_master_send(s_dev_rtdrv_eeprom->i2c_bus,
                               s_dev_rtdrv_eeprom->fix_addr | s_dev_rtdrv_eeprom->sub_addr,
                               RT_I2C_WR,
                               buf, 2) != 2)
        {
            return RET_ERROR;
        }
    }
#endif

    return RET_OK;
}

/* EEPROM 读一个字节 */
RET_STATUS dev_eeprom_read_one_byte(uint16_t rd_addr, uint8_t *data)
{
    uint8_t buf[2];

    /* 检验目标存储地址范围有效性 */
    if (rd_addr > s_dev_rtdrv_eeprom->max_addr)
    {
        return RET_ERROR;   /* 地址超出范围 */
    }

    if (s_dev_rtdrv_eeprom->init_status != INIT_SUCC)
    {
        return RET_ERROR; /* 初始化失败, 禁止使用设备 */
    }
    
    /* 判断EEPROM设备类型, 发送要读的目标存储地址 */
#if (DEV_EEPROM_TYPE > DEV_EEPROM_TYPE_16)
    {
        /* 构建数据并发送 */
        buf[0] = (wr_addr & 0xFF00) >> 8;   /* 目标存储地址 高地址 */
        buf[1] = (wr_addr & 0xFF);          /* 目标存储地址 低地址 */
        
        if (rt_i2c_master_send(s_dev_rtdrv_eeprom->i2c_bus,
                               s_dev_rtdrv_eeprom->fix_addr,
                               RT_I2C_WR,
                               buf, 2) != 2)
        {
            return RET_ERROR;
        }
    }
#else
    {
        /* 构建数据并发送 */
        s_dev_rtdrv_eeprom->sub_addr = (rd_addr / 256);  /* 计算增补地址(在rt-thread驱动中, 会自动 << 1) */
        buf[0] = (rd_addr % 256);           /* 计算地址在所在页的偏移地址 */

        if (rt_i2c_master_send(s_dev_rtdrv_eeprom->i2c_bus,
                               s_dev_rtdrv_eeprom->fix_addr | s_dev_rtdrv_eeprom->sub_addr,
                               RT_I2C_WR,
                               buf, 1) != 1)
        {
            return RET_ERROR;
        }
    }
#endif

    /* 接收一个字节 */
    if (rt_i2c_master_recv(s_dev_rtdrv_eeprom->i2c_bus,
                           s_dev_rtdrv_eeprom->fix_addr | s_dev_rtdrv_eeprom->sub_addr,
                           RT_I2C_RD,
                           data, 1) != 1)
    {
        return RET_ERROR;
    }

    return RET_OK;
}

/* EEPROM 写buf */
RET_STATUS dev_eeprom_write_buf(uint16_t wr_addr, uint8_t *wr_buf, uint16_t byte_nbr)
{
    /* 按字节写入 */
    while (1)
    {
        if (dev_eeprom_write_one_byte(wr_addr, *wr_buf) != RET_OK)
        {
            break;  /* 写入出错 */
        }

        /* 偏移 */
        wr_addr++;
        wr_buf++;

        /* 下次操作前需要延时 */
        soc_time_delay_ms(DEV_EEPROM_DELAY);

        byte_nbr--;

        if (byte_nbr == 0)
        {
            break;  /* 写入完毕 */
        }
    }

    /* 判断是否写完 */
    if (byte_nbr != 0)
    {
        return RET_ERROR;
    }

    return RET_OK;
}

/* EEPROM 读buf */
RET_STATUS dev_eeprom_read_buf(uint16_t rd_addr, uint8_t *rd_buf, uint16_t byte_nbr)
{
    /* 按字节读出 */
    while (1)
    {
        if (dev_eeprom_read_one_byte(rd_addr, rd_buf) != RET_OK)
        {
            break;  /* 读出出错 */
        }

        /* 偏移 */
        rd_addr++;
        rd_buf++;
        
        /* 下次操作前需要延时 */
        soc_time_delay_ms(DEV_EEPROM_DELAY);

        byte_nbr--;
        
        if (byte_nbr == 0)
        {
            break;  /* 写入完毕 */
        }
    }

    /* 判断是否读完 */
    if (byte_nbr != 0)
    {
        return RET_ERROR;
    }

    return RET_OK;
}

/* EEPROM 写float */
RET_STATUS dev_eeprom_write_float(uint16_t wr_addr, float data)
{
    store_float_t store_float = {0};

    store_float.data = data;

    if (dev_eeprom_write_buf(wr_addr, store_float.buf, sizeof(store_float.data)) != RET_OK)
    {
        return RET_ERROR;
    }

    return RET_OK;
}

/* EEPROM 读float */
RET_STATUS dev_eeprom_read_float(uint16_t rd_addr, float *data)
{
    store_float_t store_float = {0};

    if (dev_eeprom_read_buf(rd_addr, store_float.buf, sizeof(store_float.data)) != RET_OK)
    {
        return RET_ERROR;
    }

    *data = store_float.data;

    return RET_OK;
}

